package org.example.trade.controller;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.api.check.service.InventoryCheckFacadeService;
import org.example.api.common.constant.BusinessCode;
import org.example.api.goods.constant.GoodsType;
import org.example.api.goods.model.BaseGoodsVO;
import org.example.api.goods.service.GoodsFacadeService;
import org.example.api.inventory.request.InventoryRequest;
import org.example.api.inventory.service.InventoryFacadeService;
import org.example.api.order.OrderFacadeService;
import org.example.api.order.constant.TradeOrderState;
import org.example.api.order.model.TradeOrderVO;
import org.example.api.order.request.OrderCancelRequest;
import org.example.api.order.request.OrderCreateAndConfirmRequest;
import org.example.api.order.response.OrderResponse;
import org.example.api.pay.service.PayFacadeService;
import org.example.api.user.constant.UserType;
import org.example.base.response.SingleResponse;
import org.example.base.utils.RemoteCallWrapper;
import org.example.order.OrderException;
import org.example.order.sharding.id.DistributeID;
import org.example.order.sharding.id.WorkerIdHolder;
import org.example.order.validator.OrderCreateValidator;
import org.example.stream.producer.StreamProducer;
import org.example.trade.application.TradeApplicationService;
import org.example.trade.exception.TradeErrorCode;
import org.example.trade.exception.TradeException;
import org.example.trade.param.BuyParam;
import org.example.trade.param.CancelParam;
import org.example.web.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.math.BigDecimal;
import java.util.Date;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;

import static org.example.web.filter.TokenFilter.TOKEN_THREAD_LOCAL;

@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("trade")
public class TradeController {

    private static ThreadFactory inventoryBypassVerifyThreadFactory = new ThreadFactoryBuilder()
            .setNameFormat("inventory-bypass-verify-pool-%d").build();

    private ScheduledExecutorService scheduler = new ScheduledThreadPoolExecutor(10, inventoryBypassVerifyThreadFactory);

    @Autowired
    private OrderFacadeService orderFacadeService;

    @Autowired
    private TradeApplicationService tradeApplicationService;

    @Autowired
    private PayFacadeService payFacadeService;

    @Autowired
    private GoodsFacadeService goodsFacadeService;

    @Autowired
    private StreamProducer streamProducer;

    @Autowired
    private InventoryFacadeService inventoryFacadeService;

    @Autowired
    private OrderCreateValidator orderValidatorChain;

    @Autowired
    private InventoryCheckFacadeService inventoryCheckFacadeService;

    /**
     * 普通下单
     * @param buyParam
     * @return
     */
    @PostMapping("/normalBuy")
    public Result<String> normalBuy(@Valid @RequestBody BuyParam buyParam){
        try {
            OrderCreateAndConfirmRequest orderCreateAndConfirmRequest = getOrderCreateAndConfirmRequest(buyParam);
            orderValidatorChain.validate(orderCreateAndConfirmRequest);
            OrderResponse orderResponse = RemoteCallWrapper.call(req -> tradeApplicationService.normalBuy(req), orderCreateAndConfirmRequest, "createOrder");

            if (orderResponse.getSuccess()) {
                //同步写redis，如果失败，不阻塞流程，靠binlog同步保障
                try {
                    InventoryRequest inventoryRequest = new InventoryRequest(orderCreateAndConfirmRequest);
                    inventoryFacadeService.decrease(inventoryRequest);
                } catch (Exception e) {
                    log.error("decrease inventory from redis failed", e);
                }

                return Result.success(orderCreateAndConfirmRequest.getOrderId());
            }
        } catch (OrderException | TradeException e) {
            return Result.error(e.getErrorCode().getCode(), e.getErrorCode().getMessage());
        } catch (Exception e) {
            log.error(e.getMessage());
        }

        throw new TradeException(TradeErrorCode.ORDER_CREATE_FAILED);
    }

    /**
     * 秒杀
     * @param buyParam
     * @return
     */
    @PostMapping("/newBuyPlus")
    public Result<String> newBuyPlus(@Valid @RequestBody BuyParam buyParam) {
        try {
            OrderCreateAndConfirmRequest orderCreateAndConfirmRequest = getOrderCreateAndConfirmRequest(buyParam);
            orderValidatorChain.validate(orderCreateAndConfirmRequest);
            //本地事务执行器：OrderCreateTransactionListener  消息监听：NewBuyMsgListener or NewBuyBatchMsgListener ,
            boolean result = streamProducer.send("newBuyPlus-out-0",buyParam.getGoodsType(), JSON.toJSONString(orderCreateAndConfirmRequest));
            if (!result) {
                throw new TradeException(TradeErrorCode.ORDER_CREATE_FAILED);
            }
        } catch (OrderException | TradeException e) {
            return Result.error(e.getErrorCode().getCode(), e.getErrorCode().getMessage());
        } catch (Exception e) {
            log.error(e.getMessage());
        }

        return Result.error(TradeErrorCode.ORDER_CREATE_FAILED.getCode(), TradeErrorCode.ORDER_CREATE_FAILED.getMessage());

    }



    /**
     * 取消订单
     * @param cancelParam
     * @return
     */
    @PostMapping("/cancel")
    public Result<Boolean> cancel(@Valid @RequestBody CancelParam cancelParam) {
        String userId = (String) StpUtil.getLoginId();
        OrderCancelRequest orderCancelRequest = new OrderCancelRequest();
        orderCancelRequest.setIdentifier(cancelParam.getOrderId());
        orderCancelRequest.setOperateTime(new Date());
        orderCancelRequest.setOrderId(cancelParam.getOrderId());
        orderCancelRequest.setOperator(userId);
        orderCancelRequest.setOperatorType(UserType.CUSTOMER);
        OrderResponse orderResponse = RemoteCallWrapper.call(req -> orderFacadeService.cancel(req), orderCancelRequest, "cancelOrder");

        if (orderResponse.getSuccess()) {
            return Result.success(true);
        }

        throw new TradeException(TradeErrorCode.ORDER_CANCEL_FAILED);
    }



    @NotNull
    private OrderCreateAndConfirmRequest getOrderCreateAndConfirmRequest(BuyParam buyParam){
        String userId = (String) StpUtil.getLoginId();
        String orderId = DistributeID.generateWithSnowflake(BusinessCode.TRADE_ORDER, WorkerIdHolder.WORKER_ID, userId);
        //创建订单
        OrderCreateAndConfirmRequest orderCreateAndConfirmRequest = new OrderCreateAndConfirmRequest();
        orderCreateAndConfirmRequest.setOrderId(orderId);
        orderCreateAndConfirmRequest.setIdentifier(TOKEN_THREAD_LOCAL.get());
        orderCreateAndConfirmRequest.setBuyerId(userId);
        orderCreateAndConfirmRequest.setGoodsId(buyParam.getGoodsId());
        orderCreateAndConfirmRequest.setGoodsType(GoodsType.valueOf(buyParam.getGoodsType()));
        orderCreateAndConfirmRequest.setItemCount(buyParam.getItemCount());
        BaseGoodsVO goodsVO = goodsFacadeService.getGoods(buyParam.getGoodsId(), GoodsType.valueOf(buyParam.getGoodsType()));
        if (goodsVO == null || !goodsVO.available()) {
            throw new TradeException(TradeErrorCode.GOODS_NOT_FOR_SALE);
        }
        orderCreateAndConfirmRequest.setItemPrice(goodsVO.getPrice());
        orderCreateAndConfirmRequest.setSellerId(goodsVO.getSellerId());
        orderCreateAndConfirmRequest.setGoodsName(goodsVO.getGoodsName());
        orderCreateAndConfirmRequest.setGoodsPicUrl(goodsVO.getGoodsPicUrl());
        orderCreateAndConfirmRequest.setSnapshotVersion(goodsVO.getVersion());
        orderCreateAndConfirmRequest.setOrderAmount(orderCreateAndConfirmRequest.getItemPrice().multiply(new BigDecimal(orderCreateAndConfirmRequest.getItemCount())));
        orderCreateAndConfirmRequest.setOperator(UserType.PLATFORM.name());
        orderCreateAndConfirmRequest.setOperatorType(UserType.PLATFORM);
        orderCreateAndConfirmRequest.setOperateTime(new Date());
        return orderCreateAndConfirmRequest;
    }

}
